package evaluator

import (
	"fmt"
	"log"

	"gitee.com/chuanjinge/go-interpreter-waiig/src/monkey/ast"
	"gitee.com/chuanjinge/go-interpreter-waiig/src/monkey/object"
)

// bool对象只可能有两种，所以不管哪个AST的Bool节点想要表示为对象，都可以共享该对象
var (
	TRUE  = &object.Boolean{Value: true}
	FALSE = &object.Boolean{Value: false}
	NULL  = &object.Null{}
)

func getEnv(env *object.Environment, name string) (object.Object, bool) {
	obj, ok := env.Get(name)
	if ok {
		return obj, true
	}
	fn, ok := builtinFunc[name]
	if !ok {
		return NULL, false
	}
	return fn, true
}

func newError(format string, a ...any) *object.Error {
	return &object.Error{Message: fmt.Sprintf(format, a...)}
}

func Eval(node ast.Node, env *object.Environment) object.Object {
	// node的节点类型有好多种，比如 ast.Program/ast.ExpressionStatement/标识符/字面量
	// 不同的节点类型的处理逻辑不一样
	// 比如 ast.Program 我们要遍历他的 statements 中每一条语句
	// 对于ExpressionStatement，也有各种各样的expression，比如这条表达式如果只是一个字面量的话，直接返回就可以了
	switch node := node.(type) {
	case *ast.Program:
		log.Println("case-program")
		// program的statements包含三种语句 let语句、return语句、表达式语句(还有一种block语句在if表达式之中)
		return evalProgramStatements(node.Statements, env)
	case *ast.ExpressionStatement:
		log.Println("case-expression: ", node.Token.Literal, node.Expression.String())
		// 表达式语句种类太多，但是因为表达式语句是由各种节点组成的，所以仍然使用当前函数递归解决
		return Eval(node.Expression, env)
	case *ast.IntegerLiteral:
		log.Println("case-integer")
		// 整数字面量
		return &object.Integer{Value: node.Value}
	case *ast.StringLiteral:
		log.Println("case-string")
		// 整数字面量
		return &object.String{Value: node.Value}
	case *ast.BoolLiteral:
		log.Println("case-bool")
		return returnBooleanObj(node.Value)
	case *ast.PrefixExpression:
		log.Printf("case-prefix")
		// 前缀表达式的处理逻辑是右节点表达式仍然按照正常表达式处理Eval，得到object之后单独与操作符进行拼装操作
		right := Eval(node.Right, env)
		if right.Type() == object.ERROR_OBJ {
			return right
		}
		return evalPrefixExpression(node.Operator, right)
	case *ast.InfixExpression:
		log.Println("case-infix")
		left := Eval(node.Left, env)
		if left.Type() == object.ERROR_OBJ {
			return left
		}
		right := Eval(node.Right, env)
		if right.Type() == object.ERROR_OBJ {
			return right
		}
		return evalInfixExpression(node.Operator, left, right)
	case *ast.IfExpression:
		log.Println("case-if-else")
		// 条件表达式的值可能是布尔值，也可能是其他非NULL的值
		cond := Eval(node.Condition, env)
		if cond.Type() == object.ERROR_OBJ {
			return cond
		}
		if judgeIfCondition(cond) {
			log.Println("case-if-else:if")
			return evalBlockStatements(node.Consequence.Statements, env)
		} else if node.Alternative != nil {
			log.Println("case-if-else:else")
			return evalBlockStatements(node.Alternative.Statements, env)
		}
	case *ast.ReturnStatement:
		// return语句的返回值使用 return对象包裹，以便可以应对if语句中嵌套if语句存在多条return的情况。
		log.Println("case-return")
		retObj := Eval(node.Value, env)
		if retObj.Type() == object.ERROR_OBJ {
			return retObj
		}
		return &object.ReturnValue{Value: retObj}

	case *ast.LetStatement:
		// let语句要实现值绑定到标识符的操作
		log.Println("case-let")
		// 对 let.Value这个expression进行求值
		val := Eval(node.Value, env)
		if val.Type() == object.ERROR_OBJ {
			return val
		}
		// 将 let.Name 这个标识符于val绑定到一起，以便后续使用该标识符的地方可以得到上文信息
		// 而如果后文中使用的标识符找不到绑定信息，就报错!!!
		return env.Set(node.Name.Value, val) // let语句的求值直接响应表达式的结果即可
	case *ast.Identifier:
		// 标识符必须经过LetStatement的绑定操作才对
		obj, ok := getEnv(env, node.Value)
		if !ok {
			return newError("identifier not found: %s", node.Value)
		}
		return obj
	case *ast.FunctionalLiteral:
		log.Printf("case-functionliteral:env:%+v\n", env)
		// 对函数字面量进行求值
		// 函数体携带的环境变量应该不是全局的，而是自身独有的
		// 但是只有函数被调用之后，其持有的环境变量才能确定
		// 当前只返回 函数对象，以便在中缀表达式中可以调用该对象
		return &object.Function{
			Parameters: node.Parameters,
			Body:       node.Body,
			Env:        env, // 使用调用方传递过来的环境变量，一般来说该环境变量是函数自己的独立环境，但是通过指针指向了上级环境空间
		}
	case *ast.CallExpression:
		log.Println("case-call")
		// 当前表达式是函数调用表达式的话，调用函数求值
		// 拿到函数名字，从env中找函数体
		// !+ [fn-body]
		fnIdentifier, ok := node.Function.(*ast.Identifier)
		if !ok {
			return newError("call expression name error: %+v", node.Function)
		}
		// 从环境变量中获取并调用函数名对应的真正的函数体...
		fnName := fnIdentifier.Value
		fnBody, ok := getEnv(env, fnName)
		if !ok {
			return newError("identifier not found: %s", node.Function)
		}
		// !- [fn-body] // 实际上fnBody的操作可以通过Eval获得!!!

		// 整理参数 -- 如下这段逻辑抽象为 applyFunction (waiig中的抽象确实不错)
		argsObj := []object.Object{}
		for _, p := range node.Arguments {
			paramObj := Eval(p, env)
			if paramObj.Type() == object.ERROR_OBJ {
				return paramObj
			}
			argsObj = append(argsObj, paramObj)
		}

		log.Printf("function:-------------%+v\n", fnName)
		return applyFunction(fnBody, argsObj)
	case *ast.Array:
		log.Println("case-array")
		elements := []object.Object{}
		for _, exp := range node.Elements {
			item := Eval(exp, env)
			if item.Type() == object.ERROR_OBJ {
				return item
			}
			elements = append(elements, item)
		}
		return &object.Array{Elements: elements}
	case *ast.IndexExpression:
		log.Println("case-array/hash-index")
		indexObj := Eval(node.Index, env)
		if indexObj.Type() == object.ERROR_OBJ {
			return indexObj
		}

		arrObj := Eval(node.Name, env)
		switch obj := arrObj.(type) {
		case *object.Array:
			if indexObj.Type() != object.INTEGER_OBJ {
				return newError("array index is not interger, but %s", indexObj.Type())
			}

			idxObj, _ := indexObj.(*object.Integer)
			index := idxObj.Value
			if index >= int64(len(obj.Elements)) {
				return newError("index is out of range in array %s", arrObj.Inspect())
			}

			return obj.Elements[index]
		case *object.Hash:
			nodeHashable, ok := indexObj.(object.Hashable)
			if !ok {
				return newError("index is wrong type: %s", indexObj.Type())
			}
			hashPair, ok := obj.Pairs[nodeHashable.HashKey()]
			if !ok {
				return NULL
			}
			return hashPair.Value
		default:
			return newError("index operator not supported: %s", indexObj.Type())
		}

	case *ast.HashLiteral:
		log.Println("case-hashliteral")
		// 最终返回一个 object/Hash
		return evalHashLiteral(node, env)
	}
	return NULL
}

// 将hash语法节点转换为hash对象
func evalHashLiteral(hl *ast.HashLiteral, env *object.Environment) object.Object {
	// 多个对象分别处理
	h := &object.Hash{
		Pairs: make(map[object.HashKey]object.HashPair),
	}
	for keyExp, ValExp := range hl.Pairs {
		// 将键表达式转为键对象
		keyObj := Eval(keyExp, env)
		if keyObj.Type() == object.ERROR_OBJ {
			return keyObj
		}
		hashKey, ok := keyObj.(object.Hashable)
		if !ok {
			return newError("unusable as hash key:%s", keyObj.Type())
		}

		// 将值表达式转换为值对象
		valObj := Eval(ValExp, env)
		hashPair := object.HashPair{
			Key:   keyObj,
			Value: valObj,
		}
		h.Pairs[hashKey.HashKey()] = hashPair
	}

	return h
}

// 应用定义好的函数体执行传入的参数
// 1. 可能是自定义函数
// 2. 也可能是内置函数
func applyFunction(fn object.Object, args []object.Object) object.Object {
	switch fnObj := fn.(type) {
	case *object.Function:
		// 如果是自定义函数，则将实参绑定到形参之上写入环境变量
		if len(fnObj.Parameters) != len(args) {
			return newError("call expression parameter error: param num not match %d:%d", len(fnObj.Parameters), len(args))
		}
		// 执行callExpression中的参数列表得到真正的参数值对象
		// 将参数值对象写入到fnObj的环境变量之中，然后执行fnObj中的语句列表
		// 环境变量必须每一次call都有单独的环境变量，否则就无法保证环境的唯一性了
		// 当前函数调用应该拥有全新的环境，但是他有可能通过闭包的形式携带之前的环境变量过来，所以当前的全新环境创建的时候我们将之前的闭包环境封装进来
		// 注意，我们封装的是 fnObj.Env 而不是 env,前者是函数字面量所处的环境，而后者有可能已经跳出到全局之中的全局环境了。
		// ！！！！！！这个环境变量以后要再思考，为什么此处 env 和 fnObj.Env 会分离。
		fnEnv := object.NewEncloseEnvironment(fnObj.Env)
		for index, paramObj := range args {
			paramName := fnObj.Parameters[index].Value
			fnEnv.Set(paramName, paramObj)
		}
		// 执行语句块
		return evalBlockStatements(fnObj.Body.Statements, fnEnv)
	case *object.Builtin:
		result, err := fnObj.Fn(args...)
		if err != nil {
			return newError(err.Error())
		}
		return result
	default:
		return newError("not a function: %s", fn.Type())
	}
}

func judgeIfCondition(obj object.Object) bool {
	switch node := obj.(type) {
	case *object.Null:
		return false
	case *object.Boolean:
		if node == TRUE {
			return true
		} else {
			return false
		}
	case *object.Integer:
		if node.Value == 0 {
			return false
		} else {
			return true
		}
	default:
		log.Println("if condition cannot judge!!")
		return false
	}

}

// program的多语句理论上应该逐条执行，并且缓存执行结果，以便后续语句使用，直到最后的语句返回最终的结果
// 暂时实现依赖最后一条语句的结果（中间的缓存操作暂不执行
// -- 该方法被 evalProgramStatements 和 evalBlockStatements 代替拆分!!!
// func evalStatements(stmts []ast.Statement) object.Object {
// 	var result object.Object
// 	for _, statement := range stmts {
// 		result = Eval(statement)
// 		if node, ok := result.(*object.ReturnValue); ok {
// 			return node.Value // 如果是return语句生成的return对象就不再继续处理后续语句了
// 		}
// 	}
// 	return result
// }

// 如果是主代码区的语句列表解析得到了return对象，返回该对象包裹的真实对象
// 该方法主要与 evalBlockStatements 区别，后者针对if/else的块操作，不处理return对象，以便可以应对嵌套block下多return的情况
func evalProgramStatements(stmts []ast.Statement, env *object.Environment) object.Object {
	var result object.Object
	for _, statement := range stmts {
		result = Eval(statement, env)
		// 如下两种类型中断后续求值
		if node, ok := result.(*object.ReturnValue); ok {
			return node.Value // 如果是return语句生成的return对象就不再继续处理后续语句了
		} else if nodeError, ok := result.(*object.Error); ok {
			return nodeError
		}
	}
	return result
}

// 如果是if或else的block块的语句，碰到Return对象，直接向上抛，而且终止继续解析
// 这么解决了嵌套block下多return的情况下碰到第一个return就真的return的问题
func evalBlockStatements(stmts []ast.Statement, env *object.Environment) object.Object {
	log.Println("--block statement--")
	var result object.Object
	for _, statement := range stmts {
		result = Eval(statement, env)
		//if node, ok := result.(*object.ReturnValue); ok {
		//log.Printf("return by %s\n", statement.String())
		// 如果是return语句生成的return对象就不再继续处理后续语句了,
		// 此处直接返回return对象，而不是内部的真实对象，是为了与program的statement进行区分，以便Eval可以有不同的处理方式
		//return node
		//}
		t := result.Type()
		if t == object.RETURN_VALUE_OBJ || t == object.ERROR_OBJ {
			return result // 终端后续语句的求值
		}
	}
	return result
}

// 基于前缀运算符+右侧表达式的对象结果来计算最终的对象结果
func evalPrefixExpression(operator string, right object.Object) object.Object {
	// 根据不同的operator来执行不同的操作
	if operator == "!" {
		return evalBangPrefixExpression(right)
	} else if operator == "-" {
		return evalSubPrefixExpression(right)
	} else {
		log.Println("evalPrefixExpression operator type TODO")
	}

	// 一开始的时候返回的是NULL对象，现在改为返回ERROR对象
	return newError("unkonwn operator: %s%s", operator, right.Type())
	//return NULL // 注意在无法解析的时候使用的是NULL对象，而不是空指针
}

// 基于右侧表达式的对象结果 进行真值判断 和 取反操作
// 默认无法case的情况最终都按照false处理（也就是说本身视为真值）
func evalBangPrefixExpression(right object.Object) *object.Boolean {
	var bolRight *object.Boolean
	switch node := right.(type) {
	case *object.Integer:
		if node.Value == 0 {
			bolRight = FALSE
		} else {
			bolRight = TRUE
		}
	case *object.Boolean:
		bolRight = node
	default:
		// TODO
		log.Println("evalPrefixExpression right type TODO")
		bolRight = TRUE // 待会取反返回FALSE
	}

	if bolRight == TRUE {
		return FALSE
	} else {
		return TRUE
	}
}

// 负数运算符的求值计算，基于right这个整数对象来操作
// 由于有可能在解析的时候返回NULL对象，所以还不能把该方法的返回值固定位 Integer对象，那就返回接口类型吧
func evalSubPrefixExpression(right object.Object) object.Object {
	switch node := right.(type) {
	case *object.Integer:
		return &object.Integer{Value: -node.Value}
	default:
		return newError("unknown operator: -%s", right.Type())
		//return NULL
	}
}

// 基于中缀搞定两侧的对象的结合值
func evalInfixExpression(operator string, left object.Object, right object.Object) object.Object {
	// 中缀表达式的计算维度按照类型来搞，要不然写的很乱
	typeL := left.Type()
	typeR := right.Type()
	switch {
	case typeL == object.INTEGER_OBJ && typeR == object.INTEGER_OBJ:
		// 左右都是整数是最好处理的一种方式
		leftInt := left.(*object.Integer)
		rightInt := right.(*object.Integer)
		return evalIntegerInfixExpression(operator, leftInt, rightInt)
	case typeL == object.BOOLEAN_OBJ && typeR == object.BOOLEAN_OBJ:
		leftBol := left.(*object.Boolean)
		rightBol := right.(*object.Boolean)
		return evalBooleanInfixExpression(operator, leftBol, rightBol)
	case typeL == object.STRING_OBJ && typeR == object.STRING_OBJ:
		// 字符串操作
		leftStr := left.(*object.String)
		rightStr := right.(*object.String)
		return evalStringInfixExpression(operator, leftStr, rightStr)
	case typeL != typeR:
		return newError("type mismatch: %s %s %s", left.Type(), operator, right.Type())
	default:
		return newError("unknown operator: %s %s %s", left.Type(), operator, right.Type())
	}

	//return NULL
}

// 处理两个整数的【计算】和【比较】
func evalIntegerInfixExpression(operator string, left *object.Integer, right *object.Integer) object.Object {
	switch operator {
	case "+":
		return &object.Integer{Value: left.Value + right.Value}
	case "-":
		return &object.Integer{Value: left.Value - right.Value}
	case "*":
		return &object.Integer{Value: left.Value * right.Value}
	case "/":
		return &object.Integer{Value: left.Value / right.Value}
	case "==":
		return returnBooleanObj(left.Value == right.Value)
	case "!=":
		return returnBooleanObj(left.Value != right.Value)
	case "<":
		return returnBooleanObj(left.Value < right.Value)
	case ">":
		return returnBooleanObj(left.Value > right.Value)
	default:
		return newError("unknown operator: %s %s %s", left.Type(), operator, right.Type())
		//return NULL
	}
}

func evalBooleanInfixExpression(operator string, left *object.Boolean, right *object.Boolean) object.Object {
	switch operator {
	case "==":
		return returnBooleanObj(left == right) // 两个指针值的判断，其实是判断是否指向了相同的内存地址
	case "!=":
		return returnBooleanObj(left != right) // 两个指针值的判断，其实是判断是否指向了相同的内存地址
	default:
		return newError("unknown operator: %s %s %s", left.Type(), operator, right.Type())
		//return NULL
	}
}

// 中缀运算符连接的字符串相关的操作
// 当前仅支持+，即连接操作
func evalStringInfixExpression(operator string, left *object.String, right *object.String) object.Object {
	switch operator {
	case "+":
		return &object.String{Value: left.Value + right.Value}
	default:
		return newError("unknown operator: %s %s %s", left.Type(), operator, right.Type())
	}
}

func returnBooleanObj(astNodeValue bool) *object.Boolean {
	if astNodeValue {
		return TRUE
	} else {
		return FALSE
	}
}
